
#include    <stdio.h>
#include    <stdlib.h>
#include    <errno.h>
#include    <unistd.h>

#include    "acel/string.hpp"
#include    "acel/exception.hpp"
#include    "acel/inet_addr.hpp"

namespace   acel
{

// Transform the current address into string format.

int
INET_Addr::toString (char s[],
        size_t size,
        int ipaddr_format) const
{

    // XXX Can we (should we) include the scope id for IPv6 addresses?
    char  hoststr[MAXHOSTNAMELEN+1];

    bool result = false;
    if (ipaddr_format == 0)
        result = (this->getHostName (hoststr, MAXHOSTNAMELEN+1) == 0);
    else
        result = (this->getHostAddr (hoststr, MAXHOSTNAMELEN+1) != 0);

    if (!result)
        return -1;

    size_t total_len =
        strlen (hoststr)
        + 5 // strlen ("65535"), Assuming the max port number.
        + 1 // sizeof (':'), addr/port sep
        + 1; // sizeof ('\0'), terminating NUL
#if !defined (ACE_WIN32) && defined (ACE_USES_WCHAR)
    char const *format = ACE_TEXT("%ls:%d");
#else
    char const *format = "%s:%d";
#endif /* !ACE_WIN32 && ACE_USES_WCHAR */
#if defined (ACE_HAS_IPV6)
    if (strchr (hoststr, ACE_TEXT (':')) != 0)
    {
        total_len += 2; // strlen ("[]") IPv6 addr frames
#  if !defined (ACE_WIN32) && defined (ACE_USES_WCHAR)
        format = ACE_TEXT("[%ls]:%d");
#  else
        format = ACE_TEXT("[%s]:%d");
#  endif /* !ACE_WIN32 && ACE_USES_WCHAR */
    }
#endif // ACE_HAS_IPV6

    if (size < total_len)
        return -1;
    else
        sprintf (s, format,
                hoststr,
                this->getPortNumber ());
    return 0;
}

// Compare two addresses for inequality.

bool
INET_Addr::operator != (const INET_Addr &sap) const
{
    return !((*this) == sap);
}

// Compare two addresses for equality.

bool
INET_Addr::operator == (const INET_Addr &sap) const
{

    if (this->getType () != sap.getType ()
            || this->getSize () != sap.getSize ())
        return false;

    return (memcmp (&this->_inet_addr,
                &sap._inet_addr,
                this->getSize ()) == 0);
}

bool
INET_Addr::isIPEqual (const INET_Addr &sap) const
{
    if (this->getType () != sap.getType ()
            || this->getSize () != sap.getSize ())
        return false;

#if defined (ACE_HAS_IPV6)
    if (this->getType () == PF_INET6)
    {
        const unsigned int *addr =
            reinterpret_cast<const unsigned int*>(this->ip_addr_pointer());
        const unsigned int *saddr =
            reinterpret_cast<const unsigned int*>(sap.ip_addr_pointer());
        return (addr[0] == saddr[0] &&
                addr[1] == saddr[1] &&
                addr[2] == saddr[2] &&
                addr[3] == saddr[3]);
    }
    else
#endif /* ACE_HAS_IPV6 */
        return this->getIPAddress () == sap.getIPAddress();
}


u_long
INET_Addr::hash (void) const
{
#if defined (ACE_HAS_IPV6)
    if (this->getType () == PF_INET6)
    {
        const unsigned int *addr = (const unsigned int*)this->ip_addr_pointer();
        return addr[0] + addr[1] + addr[2] + addr[3] + this->get_port_number();
    }
    else
#endif /* ACE_HAS_IPV6 */
        return this->getIPAddress () + this->getPortNumber ();
}

INET_Addr::INET_Addr (void)
    : Addr (determineType (), sizeof (_inet_addr))
{
    this->reset ();
}

int
INET_Addr::set (const INET_Addr &sa)
{

    if (sa.getType () == AF_ANY)
        // Ugh, this is really a base class, so don't copy it.
        memset (&this->_inet_addr, 0, sizeof (this->_inet_addr));
    else
    {
        // It's ok to make the copy.
        memcpy (&this->_inet_addr,
                &sa._inet_addr,
                sa.getSize ());

        this->setType (sa.getType());
        this->setSize (sa.getSize());
    }

    return 0;
}

// Transform the string into the current addressing format.

int
INET_Addr::fromString (const char s[], int address_family)
{
    int result;
    char *ip_buf = 0;
    char *ip_addr = 0;

    // Need to make a duplicate since we'll be overwriting the string.
    ip_buf = strdup (s);
    if ( NULL == ip_buf ) {
         return   -1;
    }
    
    ip_addr = ip_buf;
    // We use strrchr because of IPv6 addresses.
    char *port_p = strrchr (ip_addr, ':');
#if defined (ACE_HAS_IPV6)
    // Check for extended IPv6 format : '[' <ipv6 address> ']' ':' <port>
    if (ip_addr[0] == '[')
    {
        // find closing bracket
        char *cp_pos = strchr (ip_addr, ']');
        // check for port separator after closing bracket
        // if not found leave it, error will come later
        if (cp_pos)
        {
            *cp_pos = '\0'; // blank out ']'
            ++ip_addr; // skip over '['
            if (cp_pos[1] == ':')
                port_p = cp_pos + 1;
            else
                port_p = cp_pos; // leads to error on missing port
        }
    }
#endif /* ACE_HAS_IPV6 */

    if (port_p == 0) // Assume it's a port number.
    {
        char *endp = 0;
        long port = strtol (ip_addr, &endp, 10);

        if (*endp == '\0')    // strtol scanned the entire string - all digits
        {
            if (port < 0 || port > ACE_MAX_DEFAULT_PORT)
                result = -1;
            else
                result = this->set (u_short (port), uint32_t (INADDR_ANY));
        }
        else // port name
            result = this->set (ip_addr, uint32_t (INADDR_ANY));
    }
    else
    {
        *port_p = '\0'; ++port_p; // skip over ':'

        char *endp = 0;
        long port = strtol (port_p, &endp, 10);

        if (*endp == '\0')    // strtol scanned the entire string - all digits
        {
            if (port < 0 || port > ACE_MAX_DEFAULT_PORT)
                result = -1;
            else
                result = this->set (u_short (port), ip_addr, 1, address_family);
        }
        else
            result = this->set (port_p, ip_addr);
    }

    free ((ip_buf));
    return result;
}

    int
INET_Addr::set (const char address[], int address_family)
{
    return this->fromString (address, address_family);
}

INET_Addr::INET_Addr (const char address[], int address_family)
    : Addr (determineType (), sizeof (_inet_addr))
{
    this->reset ();
    this->set (address, address_family);
}


// Copy constructor.

INET_Addr::INET_Addr (const INET_Addr &sa)
    : Addr (sa.getType (), sa.getSize())
{
    this->reset ();
    this->set (sa);
}

// Initializes a INET_Addr from a PORT_NUMBER and a 32 bit Internet
// address.

int
INET_Addr::set (u_short port_number,
        uint32_t inet_address,
        int encode,
        int map)
{
    this->setAddress (reinterpret_cast<const char *> (&inet_address),
            sizeof inet_address,
            encode, map);
    this->setPortNumber (port_number, encode);

    return 0;
}


// Initializes a INET_Addr from a PORT_NUMBER and the remote
// HOST_NAME.

int
INET_Addr::set (u_short port_number,
        const char host_name[],
        int encode,
        int address_family)
{

    // Yow, someone gave us a NULL host_name!
    if (host_name == 0)
    {
        errno = EINVAL;
        return -1;
    }

    memset ((void *) &this->_inet_addr,
            0,
            sizeof this->_inet_addr);

#if defined (ACE_HAS_IPV6)
    struct addrinfo hints;
    struct addrinfo *res = 0;
    int error = 0;
    memset (&hints, 0, sizeof (hints));
# if defined (ACE_USES_IPV4_IPV6_MIGRATION)
    if (address_family == AF_UNSPEC && !ACE::ipv6_enabled())
        address_family = AF_INET;
# endif /* ACE_USES_IPV4_IPV6_MIGRATION */
    if (address_family == AF_UNSPEC || address_family == AF_INET6)
    {
        hints.ai_family = AF_INET6;
        error = ::getaddrinfo (host_name, 0, &hints, &res);
        if (error)
        {
            if (address_family == AF_INET6)
            {
                if (res)
                    ::freeaddrinfo(res);
                errno = error;
                return -1;
            }
            address_family = AF_INET;
        }
    }
    if (address_family == AF_INET)
    {
        hints.ai_family = AF_INET;
        error = ::getaddrinfo (host_name, 0, &hints, &res);
        if (error)
        {
            if (res)
                ::freeaddrinfo(res);
            errno = error;
            return -1;
        }
    }
    this->set_type (res->ai_family);
    this->set_addr (res->ai_addr, res->ai_addrlen);
    this->set_port_number (port_number, encode);
    ::freeaddrinfo (res);
    return 0;
#else /* ACE_HAS_IPV6 */

    // IPv6 not supported... insure the family is set to IPv4
    address_family = AF_INET;
    this->setType (address_family);
    this->_inet_addr.in4_.sin_family = static_cast<short> (address_family);
#ifdef ACE_HAS_SOCKADDR_IN_SIN_LEN
    this->_inet_addr.in4_.sin_len = sizeof (this->_inet_addr.in4_);
#endif
    struct in_addr addrv4;
    if (inet_aton (host_name,
                &addrv4) == 1)
        return this->set (port_number,
                encode ? ACE_NTOHL (addrv4.s_addr) : addrv4.s_addr,
                encode);
    else
    {
#  if defined (ACE_VXWORKS) && defined (ACE_LACKS_GETHOSTBYNAME)
        hostent *hp = gethostbyname (host_name);
#  else
        hostent hentry;
        hostent* hp = NULL;
        char    buf[4096];
        int h_error = 0;  // Not the same as errno!

        gethostbyname_r (host_name, &hentry, buf, sizeof(buf), &hp, &h_error);
        if (hp == 0)
            errno = h_error;
#  endif /* ACE_VXWORKS */

        if (hp == 0)
        {
            return -1;
        }
        else
        {
            (void) memcpy ((void *) &addrv4.s_addr,
                    hp->h_addr,
                    hp->h_length);
            return this->set (port_number,
                    encode ? ACE_NTOHL (addrv4.s_addr) : addrv4.s_addr,
                    encode);
        }
    }
#endif /* ACE_HAS_IPV6 */
}

// Helper function to get a port number from a port name.

static int get_port_number_from_name (const char port_name[],
        const char protocol[])
{
    // Maybe port_name is directly a port number?
    char *endp = 0;
    long port_number = strtol (port_name, &endp, 10);

    if (*endp == '\0')
    {
        // port_name was really a number, and nothing else.

        // Check for overflow.
        if (port_number < 0 || port_number > ACE_MAX_DEFAULT_PORT)
            return -1;

        // Return the port number.  NOTE: this number must
        // be returned in network byte order!
        u_short n = static_cast<u_short> (port_number);
        n = ACE_HTONS (n);
        return n;
    }

    // We try to resolve port number from its name.

#if defined (ACE_LACKS_GETSERVBYNAME)
    port_number = 0;
    ACE_UNUSED_ARG (port_name);
    ACE_UNUSED_ARG (protocol);
#else
    port_number = -1;
    servent sentry;
    char buf[4096];
    servent *sp = NULL;
    getservbyname_r (port_name, protocol, &sentry, buf, sizeof(buf), &sp);
    if (sp != 0)
        port_number = sp->s_port;
#endif /* ACE_LACKS_GETSERVBYNAME */

    return port_number;
}

// Initializes a INET_Addr from a <port_name> and the remote
// <host_name>.

    int
INET_Addr::set (const char port_name[],
        const char host_name[],
        const char protocol[])
{

    int const port_number = get_port_number_from_name (port_name, protocol);
    if (port_number == -1)
    {
        ACE_NOTSUP_RETURN (-1);
    }

    int address_family = PF_UNSPEC;
#  if defined (ACE_HAS_IPV6)
    if (strcmp (ACE_TEXT_CHAR_TO_TCHAR(protocol), ACE_TEXT ("tcp6")) == 0)
        address_family = AF_INET6;
#  endif /* ACE_HAS_IPV6 */

    return this->set (static_cast<u_short> (port_number),
            host_name, 0, address_family);
}

// Initializes a INET_Addr from a <port_name> and a 32 bit
// Internet address.

    int
INET_Addr::set (const char port_name[],
        uint32_t inet_address,
        const char protocol[])
{

    int const port_number = get_port_number_from_name (port_name, protocol);
    if (port_number == -1)
    {
        ACE_NOTSUP_RETURN (-1);
    }

    return this->set (static_cast<u_short> (port_number),
            inet_address, 0);
}

// Creates a INET_Addr from a PORT_NUMBER and the remote
// HOST_NAME.

INET_Addr::INET_Addr (u_short port_number,
        const char host_name[],
        int address_family)
    : Addr (determineType (), sizeof (_inet_addr))
{
    memset (&this->_inet_addr, 0, sizeof (this->_inet_addr));
    if (this->set (port_number,
                host_name,
                1,
                address_family) == -1)
        throw Exception(
                    String::format("INET_Addr::INET_Addr: %s\n",
                    (host_name == 0) ?
                        "<unknown>" : host_name));
}

// Creates a INET_Addr from a sockaddr_in structure.

int
INET_Addr::set (const sockaddr_in *addr, int len)
{

    if (addr->sin_family == AF_INET)
    {
        int maxlen = static_cast<int> (sizeof (this->_inet_addr.in4_));
        if (len > maxlen)
            len = maxlen;
        memcpy (&this->_inet_addr.in4_, addr, len);
        this->base_set (AF_INET, len);
        return 0;
    }
#if defined (ACE_HAS_IPV6)
    else if (addr->sin_family == AF_INET6)
    {
        int maxlen = static_cast<int> (sizeof (this->_inet_addr.in6_));
        if (len > maxlen)
            len = maxlen;
        memcpy (&this->_inet_addr.in6_, addr, len);
        this->base_set (AF_INET6, len);
        return 0;
    }
#endif /* ACE_HAS_IPV6 */

    errno = EAFNOSUPPORT;
    return -1;
}

// Return the address.

void *
INET_Addr::getAddr (void) const
{
    return (void*)&this->_inet_addr;
}

    void
INET_Addr::setAddr (void *addr, int len)
{
    this->setAddr (addr, len, 0);
}

// Set a pointer to the address.
    void
INET_Addr::setAddr (void *addr, int /* len */, int map)
{
    struct sockaddr_in *getfamily = static_cast<struct sockaddr_in *> (addr);

    if (getfamily->sin_family == AF_INET)
    {
#if defined (ACE_HAS_IPV6)
        if (map)
            this->setType (AF_INET6);
        else
#endif /* ACE_HAS_IPV6 */
            this->setType (AF_INET);
        this->setPortNumber (getfamily->sin_port, 0);
        this->setAddress (reinterpret_cast<const char*> (&getfamily->sin_addr),
                sizeof (getfamily->sin_addr),
                0, map);
    }
#if defined (ACE_HAS_IPV6)
    else if (getfamily->sin_family == AF_INET6)
    {
        struct sockaddr_in6 *in6 = static_cast<struct sockaddr_in6*> (addr);
        this->set_port_number (in6->sin6_port, 0);
        this->set_address (reinterpret_cast<const char*> (&in6->sin6_addr),
                sizeof (in6->sin6_addr),
                0);
        this->_inet_addr.in6_.sin6_scope_id = in6->sin6_scope_id;
    }
#endif // ACE_HAS_IPV6
}

// Creates a INET_Addr from a sockaddr_in structure.

INET_Addr::INET_Addr (const sockaddr_in *addr, int len)
    : Addr (determineType (), sizeof (_inet_addr))
{
    this->reset ();
    this->set (addr, len);
}

// Creates a INET_Addr from a PORT_NUMBER and an Internet address.

INET_Addr::INET_Addr (u_short port_number,
        uint32_t inet_address)
    : Addr (determineType (), sizeof (_inet_addr))
{
    this->reset ();
    if (this->set (port_number, inet_address) == -1)
        throw Exception ("INET_Addr::INET_Addr");
}

// Creates a INET_Addr from a PORT_NAME and the remote
// HOST_NAME.

INET_Addr::INET_Addr (const char port_name[],
        const char host_name[],
        const char protocol[])
    : Addr (determineType (), sizeof (_inet_addr))
{
    this->reset ();
    if (this->set (port_name,
                host_name,
                protocol) == -1)
        throw Exception ("INET_Addr::INET_Addr");
}

// Creates a INET_Addr from a PORT_NAME and an Internet address.

INET_Addr::INET_Addr (const char port_name[],
        uint32_t inet_address,
        const char protocol[])
    : Addr (determineType (), sizeof (_inet_addr))
{
#if !defined (ACE_LACKS_HTONL)
    this->reset ();
    if (this->set (port_name,
                htonl (inet_address),
                protocol) == -1)
        throw Exception("INET_Addr::INET_Addr");
#else
    ACE_UNUSED_ARG (port_name);
    ACE_UNUSED_ARG (inet_address);
    ACE_UNUSED_ARG (protocol);
#endif
}

INET_Addr::~INET_Addr (void)
{
}

int
INET_Addr::getHostName (char hostname[],
        size_t len) const
{

    int result;
    if (len > 1)
    {
        result = this->getHostName_i (hostname,len);
        if (result < 0)
        {
            if (result == -2)
                // We know that hostname is nul-terminated
                result = -1;
            else
            {
                //result == -1;
                // This could be worse than hostname[len -1] = '\0'?
                hostname[0] = '\0';
            }
        }
    }
    else
    {
        if (len == 1)
            hostname[0] = '\0';
        result = -1;
    }

    return result;
}

// Return the character representation of the hostname.

const char *
INET_Addr::getHostName (void) const
{

    static char name[MAXHOSTNAMELEN + 1];
    if (this->getHostName (name, MAXHOSTNAMELEN + 1) == -1)
        strcpy (name, "<unknown>");
    return name;
}

    void
INET_Addr::setPortNumber (u_short port_number,
        int encode)
{

    if (encode)
        port_number = ACE_HTONS (port_number);

#if defined (ACE_HAS_IPV6)
    if (this->getType () == AF_INET6)
        this->_inet_addr.in6_.sin6_port = port_number;
    else
#endif /* ACE_HAS_IPV6 */
        this->_inet_addr.in4_.sin_port = port_number;
}

// returns -2 when the hostname is truncated
int
INET_Addr::getHostName_i (char hostname[], size_t len) const
{

#if defined (ACE_HAS_IPV6)
    if ((this->getType () == PF_INET6 &&
                0 == memcmp (&this->_inet_addr.in6_.sin6_addr,
                    &in6addr_any,
                    sizeof (this->_inet_addr.in6_.sin6_addr)))
            ||
            (this->getType () == PF_INET &&
             this->_inet_addr.in4_.sin_addr.s_addr == INADDR_ANY))
#else
        if (this->_inet_addr.in4_.sin_addr.s_addr == INADDR_ANY)
#endif /* ACE_HAS_IPV6 */
        {
            if (gethostname (hostname, len) == -1)
                return -1;
            else
                return 0;
        }
        else
        {
#if defined (ACE_VXWORKS) && defined (ACE_LACKS_GETHOSTBYADDR)
            ACE_UNUSED_ARG (len);
            int error =
                ::hostGetByAddr ((int) this->_inet_addr.in4_.sin_addr.s_addr,
                        hostname);
            if (error == OK)
                return 0;
            else
            {
                errno = error;
                return -1;
            }
#else
            void* addr = this->IPAddrPointer ();
            int   size = this->IPAddrSize ();
            int   type = this->getType ();

#  if defined (ACE_HAS_IPV6) && defined (ACE_HAS_BROKEN_GETHOSTBYADDR_V4MAPPED)
            // Most OS can not handle IPv6-mapped-IPv4 addresses (even
            // though they are meant to) so map them back to IPv4 addresses
            // before trying to resolve them
            in_addr demapped_addr;
            if (type == PF_INET6 &&
                    (this->is_ipv4_mapped_ipv6 () || this->is_ipv4_compat_ipv6 ()))
            {
                memcpy (&demapped_addr.s_addr, &this->_inet_addr.in6_.sin6_addr.s6_addr[12], 4);
                addr = &demapped_addr;
                size = sizeof(demapped_addr);
                type = PF_INET;
            }
#  endif /* ACE_HAS_IPV6 */

#  if defined (DIGITAL_UNIX) && defined (__GNUC__)
            hostent * const hp =
                gethostbyaddr (static_cast <char *> (addr), size, type);
#  else
            int h_error;  // Not the same as errno!
            hostent hentry;
            char buf[4096];
            hostent * hp = NULL;
            gethostbyaddr_r (static_cast <char *> (addr),
                        size,
                        type,
                        &hentry,
                        buf,
                        sizeof(buf),
                        &hp,
                        &h_error);
#  endif /* DIGITAL_UNIX */

            if (hp == 0 || hp->h_name == 0)
                return -1;

            if (strlen (hp->h_name) >= len)
            {
                // We know the length, so use memcpy
                if (len > 0)
                {
                    memcpy (hostname, hp->h_name, len - 1);
                    hostname[len-1]= '\0';
                }
                errno = ENOSPC;
                return -2;  // -2 Means that we have a good string
                // Using errno looks ok, but ENOSPC could be set on
                // other places.
            }

            strcpy (hostname, hp->h_name);
            return 0;
#endif /* ACE_VXWORKS */
        }
}

int INET_Addr::setAddress (const char *ip_addr,
        int len,
        int encode /* = 1 */,
        int map /* = 0 */)
{
    // This is really intended for IPv4. If the object is IPv4, or the type
    // hasn't been set but it's a 4-byte address, go ahead. If this is an
    // IPv6 object and <encode> is requested, refuse.
    if (encode && len != 4)
    {
        errno = EAFNOSUPPORT;
        return -1;
    }

    if (len == 4)
    {
        uint32_t ip4 = *reinterpret_cast<const uint32_t *> (ip_addr);
        if (encode)
            ip4 = ACE_HTONL (ip4);


        if (this->getType () == AF_INET && map == 0) {
            this->base_set (AF_INET, sizeof (this->_inet_addr.in4_));
#ifdef ACE_HAS_SOCKADDR_IN_SIN_LEN
            this->_inet_addr.in4_.sin_len = sizeof (this->_inet_addr.in4_);
#endif
            this->_inet_addr.in4_.sin_family = AF_INET;
            this->setSize (sizeof (this->_inet_addr.in4_));
            memcpy (&this->_inet_addr.in4_.sin_addr,
                    &ip4,
                    len);
        }
#if defined (ACE_HAS_IPV6)
        else if (map == 0)
        {
            // this->set_type (AF_INET);
            this->base_set (AF_INET, sizeof (this->_inet_addr.in4_));
#ifdef ACE_HAS_SOCKADDR_IN_SIN_LEN
            this->_inet_addr.in4_.sin_len = sizeof (this->_inet_addr.in4_);
#endif
            this->_inet_addr.in4_.sin_family = AF_INET;
            this->set_size (sizeof (this->_inet_addr.in4_));
            memcpy (&this->_inet_addr.in4_.sin_addr,
                    &ip4, len);
        }
        // If given an IPv4 address to copy to an IPv6 object, map it to
        // an IPv4-mapped IPv6 address.
        else
        {
            this->base_set (AF_INET6, sizeof (this->_inet_addr.in6_));
#ifdef ACE_HAS_SOCKADDR_IN6_SIN6_LEN
            this->_inet_addr.in6_.sin6_len = sizeof (this->_inet_addr.in6_);
#endif
            this->_inet_addr.in6_.sin6_family = AF_INET6;
            this->set_size (sizeof (this->_inet_addr.in6_));
            if (ip4 == ACE_HTONL (INADDR_ANY))
            {
                in6_addr const ip6 = in6addr_any;
                memcpy (&this->_inet_addr.in6_.sin6_addr,
                        &ip6,
                        sizeof (ip6));
                return 0;
            }

            // Build up a 128 bit address.  An IPv4-mapped IPv6 address
            // is defined as 0:0:0:0:0:ffff:IPv4_address.  This is defined
            // in RFC 1884 */
            memset (&this->_inet_addr.in6_.sin6_addr, 0, 16);
            this->_inet_addr.in6_.sin6_addr.s6_addr[10] =
                this->_inet_addr.in6_.sin6_addr.s6_addr[11] = 0xff;
            memcpy
                (&this->_inet_addr.in6_.sin6_addr.s6_addr[12], &ip4, 4);
        }
#endif /* ACE_HAS_IPV6 */
        return 0;
    }   /* end if (len == 4) */
#if defined (ACE_HAS_IPV6)
    else if (len == 16)
    {
        if (this->getType () != PF_INET6)
        {
            errno = EAFNOSUPPORT;
            return -1;
        }
        // We protect ourselves up above so IPv6 must be possible here.
        this->base_set (AF_INET6, sizeof (this->_inet_addr.in6_));
        this->_inet_addr.in6_.sin6_family = AF_INET6;
#ifdef ACE_HAS_SOCKADDR_IN6_SIN6_LEN
        this->_inet_addr.in6_.sin6_len = sizeof (this->_inet_addr.in6_);
#endif
        memcpy (&this->_inet_addr.in6_.sin6_addr, ip_addr, len);

        return 0;
    } /* end len == 16 */
#endif /* ACE_HAS_IPV6 */

    // Here with an unrecognized length.
    errno = EAFNOSUPPORT;
    return -1;

}

#if (defined (__linux__) || defined (ACE_WIN32)) && defined (ACE_HAS_IPV6)
    int
INET_Addr::setInterface (const char *intf_name)
{
    if (this->getType () == PF_INET6 &&
            (IN6_IS_ADDR_LINKLOCAL (&this->_inet_addr.in6_.sin6_addr) ||
             IN6_IS_ADDR_MC_LINKLOCAL (&this->_inet_addr.in6_.sin6_addr)))
    {
#if defined (__linux__)
        this->_inet_addr.in6_.sin6_scope_id =
            if_nametoindex (intf_name);
#else
        this->_inet_addr.in6_.sin6_scope_id =
            intf_name ? atoi (intf_name) : 0;
#endif
        // check to see if the interface lookup succeeded
        if (this->_inet_addr.in6_.sin6_scope_id != 0)
            return 0;
        else
            return -1;
    }
    else
        return 0;

}
#endif /* __linux && ACE_HAS_IPV6 */

const char *
INET_Addr::getHostAddr (char *dst, int size) const
{
#if defined (ACE_HAS_IPV6)
    if (this->getType () == AF_INET6)
    {
        // mcorino@remedy.nl - Aug-26, 2005
        // I don't think this should be done because it results in a decimal address
        // representation which is not distinguishable from the IPv4 form which makes
        // it impossible to resolve back to an IPv6 INET_Addr without prior knowledge
        // that this was such an address to begin with.

        //if (IN6_IS_ADDR_V4MAPPED (&this->_inet_addr.in6_.sin6_addr))
        //{
        //  uint32_t addr;
        //  addr = this->get_ip_address();
        //  addr = ACE_HTONL (addr);
        //  return inet_ntop (AF_INET, &addr, dst, size);
        //}

#  if defined (ACE_WIN32)
        if (0 == ::getnameinfo (reinterpret_cast<const sockaddr*> (&this->_inet_addr.in6_),
                    this->getSize (),
                    dst,
                    size,
                    0, 0,    // Don't want service name
                    NI_NUMERICHOST))
            return dst;
        ACE_OS::set_errno_to_wsa_last_error ();
        return 0;
#  else
        const char *ch = inet_ntop (AF_INET6,
                &this->_inet_addr.in6_.sin6_addr,
                dst,
                size);
#if defined (__linux__)
        if ((IN6_IS_ADDR_LINKLOCAL (&this->_inet_addr.in6_.sin6_addr) ||
                    IN6_IS_ADDR_MC_LINKLOCAL (&this->_inet_addr.in6_.sin6_addr)) &&
                this->_inet_addr.in6_.sin6_scope_id != 0)
        {
            char scope_buf[32];
            sprintf (scope_buf, "%%%u", this->_inet_addr.in6_.sin6_scope_id);
            if ((strlen (ch)+strlen (scope_buf)) < (size_t)size)
            {
                strcat (dst, scope_buf);
            }
        }
#endif
        return ch;
#  endif /* ACE_WIN32 */
    }
#endif /* ACE_HAS_IPV6 */

#if defined (ACE_VXWORKS)
    ACE_UNUSED_ARG (dst);
    ACE_UNUSED_ARG (size);

    // It would be nice to be able to encapsulate this into
    // inet_ntoa(), but that would lead to either inefficiencies
    // on vxworks or lack of thread safety.
    //
    // So, we use the way that vxworks suggests.
    INET_Addr *ncthis = const_cast<INET_Addr *> (this);
    inet_ntoa_b(this->_inet_addr.in4_.sin_addr, ncthis->buf_);
    strsncpy (dst, &buf_[0], size);
    return &buf_[0];
#else /* ACE_VXWORKS */
    char *ch = inet_ntoa (this->_inet_addr.in4_.sin_addr);
    strncpy (dst, ch, size);
    return ch;
#endif
}

// Return the dotted Internet address.
const char *
INET_Addr::getHostAddr (void) const
{
#if defined (ACE_HAS_IPV6)
    static char buf[INET6_ADDRSTRLEN];
    return this->get_host_addr (buf, INET6_ADDRSTRLEN);
#else /* ACE_HAS_IPV6 */
#  if defined (ACE_VXWORKS)
    // It would be nice to be able to encapsulate this into
    // inet_ntoa(), but that would lead to either inefficiencies
    // on vxworks or lack of thread safety.
    //
    // So, we use the way that vxworks suggests.
    INET_Addr *ncthis = const_cast<INET_Addr *> (this);
    inet_ntoa_b (this->_inet_addr.in4_.sin_addr, ncthis->buf_);
    return &buf_[0];
#  else /* ACE_VXWORKS */
    return inet_ntoa (this->_inet_addr.in4_.sin_addr);
#  endif /* !ACE_VXWORKS */
#endif /* !ACE_HAS_IPV6 */
}

// Return the 4-byte IP address, converting it into host byte order.
uint32_t
INET_Addr::getIPAddress (void) const
{
#if defined (ACE_HAS_IPV6)
    if (this->getType () == AF_INET6)
    {
        if (IN6_IS_ADDR_V4MAPPED (&this->_inet_addr.in6_.sin6_addr) ||
                IN6_IS_ADDR_V4COMPAT (&this->_inet_addr.in6_.sin6_addr)    )
        {
            uint32_t addr;
            // Return the last 32 bits of the address
            char *thisaddrptr = (char*)this->ip_addr_pointer ();
            thisaddrptr += 128/8 - 32/8;
            memcpy (&addr, thisaddrptr, sizeof (addr));
            return ACE_NTOHL (addr);
        }

        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("INET_Addr::get_ip_address: address is a IPv6 address not IPv4\n")));
        errno = EAFNOSUPPORT;
        return 0;
    }
#endif /* ACE_HAS_IPV6 */
    return ACE_NTOHL (uint32_t (this->_inet_addr.in4_.sin_addr.s_addr));
}

}   // namespace acel

